package app

import (
	"encoding/json"
	"fmt"
	"runtime"
)

// @lucas 2020
// @title

// 错误追踪栈
type ErrMsgTrace struct {
	Index int
	File  string
	Func  string
	Line  int
	Msg   string
}

// 打印一条错误
func (Emt ErrMsgTrace) PrintlnErrorMsg() {
	fmt.Printf("|%20s|%5d|%15s|%s", Emt.File, Emt.Line, Emt.Func, Emt.Msg)
	return
}

// 打印一条错误
func (Emt ErrMsgTrace) ConnectErrorMsg() string {
	str := fmt.Sprintf("|%20s|%5d|%15s|%s", Emt.File, Emt.Line, Emt.Func, Emt.Msg)
	return str
}

// 错误
type Err struct {
	Deep      int
	Code      int
	Msg       string
	MsgTrace  []ErrMsgTrace
	Type      string
	ShowTrace bool
	File      string
	Func      string
	Line      int
}

// @Summary Print Error By Println
// @Description
func (e *Err) Error() string {
	if e.ShowTrace {
		var traceArr []string
		for _, v := range e.MsgTrace {
			traceArr = append(traceArr, v.ConnectErrorMsg())
		}
		jsonStr, _ := json.Marshal(traceArr)
		return string(jsonStr)
	}

	return e.Msg
}

// @Summary Print Error By Println
// @Description
func (e *Err) PrintError() {
	for _, v := range e.MsgTrace {
		fmt.Println(v)
	}
	return
}

// @Summary 包装新错误
func NewError(mixedValue interface{}) *Err {
	err := &Err{
		Msg: "",
	}

	var msg string
	switch v := mixedValue.(type) {
	case Err:
		msg = mixedValue.(Err).Msg
		err.MsgTrace = mixedValue.(Err).MsgTrace
	case error:
		msg = mixedValue.(error).Error()
	case string:
		msg = mixedValue.(string)
	case map[string]string:
		tmp, _ := json.Marshal(mixedValue.(map[string]string))
		msg = string(tmp)
	case []string:
		tmp, _ := json.Marshal(mixedValue.([]string))
		msg = string(tmp)
	default:
		msg = fmt.Sprintf("不支持的类型 %T", v)
	}

	err.Msg = msg

	// 如果是debug模式下 开启错误追踪
	if ok := Debug(); ok {
		fu, fi, l := ErrorCaller(2)

		// 构建错误来源
		msgTrace := new(ErrMsgTrace)
		msgTrace.File = parseFilePath(fi)
		msgTrace.Func = fu
		msgTrace.Line = l
		msgTrace.Msg = msg

		err.MsgTrace = append(err.MsgTrace, *msgTrace)
		err.ShowTrace = true
	}

	return err
}

// 获取错误相关信息
func ErrorCaller(step int) (fu string, fi string, l int) {
	pc, file, line, ok := runtime.Caller(step)
	if ok {
		function := ""
		if fn := runtime.FuncForPC(pc); fn == nil {
			function = "unknown"
		} else {
			function = fn.Name()
		}

		return function, file, line
	}

	return "-", "-", 0
}

// ＠Summary Delete base path
func parseFilePath(path string) string {
	// 默认的不需要分解
	if path == "-" {
		return path
	}

	return path
}
